Skip to content

Add safety preconditions to core/src/iter/range.rs #331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

tautschnig
Copy link
Member

For the signed and unsigned methods in the macros, I added contracts that require the checked operations to succeed, which is what the safety comments are stating.

These contracts formalize the safety requirements that were previously only documented in comments.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

For the signed and unsigned methods in the macros, I added contracts
that require the checked operations to succeed, which is what the safety
comments are stating.

These contracts formalize the safety requirements that were previously
only documented in comments.
@tautschnig tautschnig requested a review from a team as a code owner April 18, 2025 14:51
@tautschnig tautschnig marked this pull request as draft April 22, 2025 18:57
@tautschnig tautschnig self-assigned this Apr 22, 2025
@tautschnig tautschnig marked this pull request as ready for review April 30, 2025 17:27
@carolynzech
Copy link

Moving to draft until our Kani version includes model-checking/kani#4043.

@carolynzech carolynzech marked this pull request as draft May 8, 2025 15:44
@tautschnig tautschnig marked this pull request as ready for review May 20, 2025 17:39
@@ -494,6 +502,13 @@ impl Step for char {
Some(unsafe { char::from_u32_unchecked(res) })
}

#[requires((start as u32).checked_add(count as u32).is_some())]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[requires((start as u32).checked_add(count as u32).is_some())]

Isn't this redundant, given that the next precondition has the same check?

Comment on lines +507 to +510
let dist = (start as u32).checked_add(count as u32);
dist.is_some() &&
((start as u32) >= 0xD800 || dist.unwrap() < 0xD800 ||
dist.unwrap().checked_add(0x800).is_some())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use is_some_and

Suggested change
let dist = (start as u32).checked_add(count as u32);
dist.is_some() &&
((start as u32) >= 0xD800 || dist.unwrap() < 0xD800 ||
dist.unwrap().checked_add(0x800).is_some())
(start as u32).checked_add(count as u32).is_some_and(|dist|
(start as u32) >= 0xD800 ||
dist < 0xD800 ||
dist.checked_add(0x800).is_some()
)

Comment on lines +528 to +533
#[requires({
let dist = (start as u32).checked_sub(count as u32);
dist.is_some() &&
((start as u32) < 0xE000 || dist.unwrap() >= 0xE000 ||
dist.unwrap().checked_sub(0x800).is_some())
})]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[requires({
let dist = (start as u32).checked_sub(count as u32);
dist.is_some() &&
((start as u32) < 0xE000 || dist.unwrap() >= 0xE000 ||
dist.unwrap().checked_sub(0x800).is_some())
})]
#[requires({
(start as u32).checked_sub(count as u32).is_some_and(|dist|
(start as u32) < 0xE000 ||
dist >= 0xE000 ||
dist.checked_sub(0x800).is_some()
)
})]

@@ -548,6 +569,7 @@ impl Step for AsciiChar {
Some(unsafe { AsciiChar::from_u8_unchecked(end) })
}

#[requires((start.to_u8() as u32).checked_add(count as u32).is_some())]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this enough? The precondition is that "the result is a valid ASCII character," so wouldn't we need a precondition to encode that expectation? I'm envisioning 1) doing the computation in a safe way 2) asserting that the outcome of that is valid ASCII?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for the trait says:

Safety
It is undefined behavior for this operation to overflow the range of values supported by Self. If you cannot guarantee that this will not overflow, use forward or forward_checked instead.

and the documentation for AsciiCharsays:

/// # Layout
///
/// This type is guaranteed to have a size and alignment of 1 byte.

This precondition says that the result doesn't overflow a u32, but shouldn't it say that it doesn't overflow "the range of values supported by Self," i.e. a u8?

@@ -558,6 +580,7 @@ impl Step for AsciiChar {
unsafe { AsciiChar::from_u8_unchecked(end) }
}

#[requires((start.to_u8() as u32).checked_sub(count as u32).is_some())]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above re: precondition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants